from flask import Flask, render_template, request, send_from_directory, jsonify
from flask_restx import Api, Resource, fields, Namespace
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
import os
import uuid
import comtypes.client
import time
import pythoncom
import traceback
import logging
import math
import re

# 配置日志
logging.basicConfig(level=logging.DEBUG, 
                   format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['OUTPUT_FOLDER'] = 'output'
app.config['ALLOWED_EXTENSIONS'] = {'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'}
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024  # 限制上传文件大小为50MB

# 确保上传和输出目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True)

# =====================
# 1. 网页：Office转PDF
# =====================
@app.route('/')
def index():
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_file():
    logger.info("收到文件上传请求")
    if 'file' not in request.files:
        logger.warning("没有上传文件")
        return jsonify({'success': False, 'error': '没有上传文件'})
    file = request.files['file']
    if file.filename == '':
        logger.warning("未选择文件")
        return jsonify({'success': False, 'error': '未选择文件'})
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        unique_filename = f"{str(uuid.uuid4())}_{filename}"
        file_path = os.path.join(os.path.abspath(app.config['UPLOAD_FOLDER']), unique_filename)
        file.save(file_path)
        pdf_filename = f"{os.path.splitext(unique_filename)[0]}.pdf"
        pdf_path = os.path.join(os.path.abspath(app.config['OUTPUT_FOLDER']), pdf_filename)
        if convert_to_pdf(file_path, pdf_path)[0]:
            return jsonify({
                'success': True, 
                'filename': pdf_filename,
                'download_url': f'/download/{pdf_filename}'
            })
        else:
            return jsonify({'success': False, 'error': '文件转换失败'})
    logger.warning("不支持的文件类型")
    return jsonify({'success': False, 'error': '不支持的文件类型'})

@app.route('/download/<filename>')
def download_file(filename):
    return send_from_directory(app.config['OUTPUT_FOLDER'], filename, as_attachment=True)

@app.route('/favicon.ico')
def favicon():
    return send_from_directory(os.path.join(app.root_path, 'static'),
                               'favicon.ico', mimetype='image/vnd.microsoft.icon')

# =====================
# 2. 网页：iOS风格计算器
# =====================
@app.route('/calculator')
def calculator_page():
    return render_template('calculator.html')

# =====================
# 3. 计算器API（用于网页计算器）
# =====================
class Calculator:
    @staticmethod
    def evaluate_expression(expression):
        try:
            expression = expression.replace(' ', '')
            if not re.match(r'^[0-9+\-*/().√πe\s]+$', expression):
                return "错误：包含非法字符"
            expression = expression.replace('π', str(math.pi))
            expression = expression.replace('e', str(math.e))
            expression = expression.replace('√', 'math.sqrt')
            allowed_names = {
                "__builtins__": {},
                "math": math,
                "abs": abs,
                "round": round,
                "pow": pow,
                "sqrt": math.sqrt,
                "sin": math.sin,
                "cos": math.cos,
                "tan": math.tan,
                "log": math.log,
                "log10": math.log10,
                "exp": math.exp,
                "pi": math.pi,
                "e": math.e
            }
            result = eval(expression, allowed_names, {})
            if isinstance(result, (int, float)):
                if isinstance(result, float):
                    if result.is_integer():
                        return str(int(result))
                    else:
                        return f"{result:.10g}"
                return str(result)
            else:
                return "错误：无效结果"
        except ZeroDivisionError:
            return "错误：除零"
        except ValueError as e:
            return f"错误：数值错误 - {str(e)}"
        except SyntaxError:
            return "错误：语法错误"
        except Exception as e:
            return f"错误：{str(e)}"

@app.route('/calculator/calculate', methods=['POST'])
def calculator_api():
    data = request.get_json()
    expression = data.get('expression', '')
    if not expression:
        return jsonify({'success': False, 'error': '表达式为空'})
    result = Calculator.evaluate_expression(expression)
    if result.startswith('错误：'):
        return jsonify({'success': False, 'error': result})
    else:
        return jsonify({'success': True, 'result': result})

# =====================
# 4. API接口（Swagger风格）
# =====================
api = Api(
    app,
    version='1.0',
    title='Office转PDF API',
    description='一个功能强大的Office文档转PDF的RESTful API服务',
    doc='/api/docs/',  # API文档路径
    prefix='/api/v1'
)

convert_ns = Namespace('convert', description='文档转换相关接口')
download_ns = Namespace('download', description='文件下载相关接口')
health_ns = Namespace('health', description='健康检查接口')
api.add_namespace(convert_ns)
api.add_namespace(download_ns)
api.add_namespace(health_ns)

upload_response_model = api.model('UploadResponse', {
    'success': fields.Boolean(required=True, description='操作是否成功'),
    'message': fields.String(description='响应消息'),
    'filename': fields.String(description='生成的PDF文件名'),
    'download_url': fields.String(description='下载链接'),
    'file_size': fields.Integer(description='文件大小(字节)'),
    'conversion_time': fields.Float(description='转换耗时(秒)')
})

error_response_model = api.model('ErrorResponse', {
    'success': fields.Boolean(required=True, description='操作是否成功', default=False),
    'error': fields.String(required=True, description='错误信息'),
    'error_code': fields.String(description='错误代码')
})

health_response_model = api.model('HealthResponse', {
    'status': fields.String(required=True, description='服务状态'),
    'timestamp': fields.String(required=True, description='检查时间'),
    'version': fields.String(required=True, description='API版本'),
    'uptime': fields.Float(description='运行时间(秒)')
})

upload_parser = api.parser()
upload_parser.add_argument('file', location='files', type=FileStorage, required=True, 
                          help='要转换的Office文档文件 (支持: .doc, .docx, .xls, .xlsx, .ppt, .pptx)')

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']

def convert_to_pdf(input_file, output_file):
    logger.info(f"开始转换文件: {input_file} 到 {output_file}")
    start_time = time.time()
    pythoncom.CoInitialize()
    file_ext = input_file.rsplit('.', 1)[1].lower()
    try:
        if file_ext in ['doc', 'docx']:
            logger.info("使用Word转换文档")
            word = comtypes.client.CreateObject('Word.Application')
            word.Visible = False
            doc = word.Documents.Open(input_file)
            doc.SaveAs(output_file, FileFormat=17)
            doc.Close()
            word.Quit()
        elif file_ext in ['xls', 'xlsx']:
            logger.info("使用Excel转换文档")
            excel = comtypes.client.CreateObject('Excel.Application')
            excel.Visible = False
            wb = excel.Workbooks.Open(input_file)
            wb.ExportAsFixedFormat(0, output_file)
            wb.Close()
            excel.Quit()
        elif file_ext in ['ppt', 'pptx']:
            logger.info("使用PowerPoint转换文档")
            powerpoint = comtypes.client.CreateObject('Powerpoint.Application')
            powerpoint.Visible = False
            presentation = powerpoint.Presentations.Open(input_file)
            presentation.ExportAsFixedFormat(output_file, 2)
            presentation.Close()
            powerpoint.Quit()
        conversion_time = time.time() - start_time
        logger.info(f"文件转换成功，耗时: {conversion_time:.2f}秒")
        return True, conversion_time
    except Exception as e:
        logger.error(f"转换错误: {e}")
        logger.error(traceback.format_exc())
        return False, 0
    finally:
        pythoncom.CoUninitialize()

@health_ns.route('/check')
class HealthCheck(Resource):
    @api.doc('health_check')
    @api.marshal_with(health_response_model)
    def get(self):
        import datetime
        return {
            'status': 'healthy',
            'timestamp': datetime.datetime.now().isoformat(),
            'version': '1.0',
            'uptime': time.time()
        }

@convert_ns.route('/upload')
class ConvertDocument(Resource):
    @api.doc('convert_document')
    @api.expect(upload_parser)
    def post(self):
        logger.info("收到文件上传请求")
        if 'file' not in request.files:
            logger.warning("没有上传文件")
            api.abort(400, '没有上传文件', error_code='NO_FILE')
        file = request.files['file']
        if file.filename == '':
            logger.warning("未选择文件")
            api.abort(400, '未选择文件', error_code='EMPTY_FILENAME')
        if not allowed_file(file.filename):
            logger.warning("不支持的文件类型")
            api.abort(400, '不支持的文件类型。支持格式: .doc, .docx, .xls, .xlsx, .ppt, .pptx', 
                     error_code='UNSUPPORTED_FORMAT')
        try:
            filename = secure_filename(file.filename)
            logger.info(f"处理文件: {filename}")
            unique_filename = f"{str(uuid.uuid4())}_{filename}"
            file_path = os.path.join(os.path.abspath(app.config['UPLOAD_FOLDER']), unique_filename)
            file.save(file_path)
            logger.info(f"文件已保存到: {file_path}")
            pdf_filename = f"{os.path.splitext(unique_filename)[0]}.pdf"
            pdf_path = os.path.join(os.path.abspath(app.config['OUTPUT_FOLDER']), pdf_filename)
            success, conversion_time = convert_to_pdf(file_path, pdf_path)
            if success:
                logger.info(f"文件转换成功: {pdf_path}")
                file_size = os.path.getsize(pdf_path) if os.path.exists(pdf_path) else 0
                
                # 构建返回结果
                result = {
                    'success': True,
                    'message': '文档转换成功',
                    'filename': pdf_filename,
                    'download_url': f'/api/v1/download/file/{pdf_filename}',
                    'file_size': file_size,
                    'conversion_time': round(conversion_time, 2)
                }
                
                # 添加调试日志
                logger.info(f"API返回结果: {result}")
                return jsonify(result)
            else:
                logger.error("文件转换失败")
                api.abort(500, '文件转换失败，请检查文件格式或稍后重试', error_code='CONVERSION_FAILED')
        except Exception as e:
            logger.error(f"处理文件时发生错误: {e}")
            logger.error(traceback.format_exc())
            api.abort(500, f'服务器内部错误: {str(e)}', error_code='INTERNAL_ERROR')

@download_ns.route('/file/<filename>')
class DownloadFile(Resource):
    @api.doc('download_file')
    @api.param('filename', 'PDF文件名', required=True)
    def get(self, filename):
        try:
            return send_from_directory(app.config['OUTPUT_FOLDER'], filename, as_attachment=True)
        except FileNotFoundError:
            api.abort(404, '文件不存在', error_code='FILE_NOT_FOUND')

@convert_ns.route('/formats')
class SupportedFormats(Resource):
    @api.doc('get_supported_formats')
    def get(self):
        return {
            'supported_formats': {
                'word': ['.doc', '.docx'],
                'excel': ['.xls', '.xlsx'],
                'powerpoint': ['.ppt', '.pptx']
            },
            'max_file_size': '50MB',
            'description': '支持Microsoft Office文档转换为PDF格式'
        }

@app.after_request
def after_request(response):
    response.headers.add('Access-Control-Allow-Origin', '*')
    response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
    response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
    return response

if __name__ == '__main__':
    print("=" * 60)
    print("🚀 Office转PDF/计算器/Swagger API服务启动 (端口5000)")
    print("=" * 60)
    print("🌐 Office转PDF网页: http://localhost:5000/")
    print("🧮 计算器网页: http://localhost:5000/calculator")
    print("📖 Swagger API文档: http://localhost:5000/api/docs/")
    print("🔗 API根路径: http://localhost:5000/api/v1/")
    print("=" * 60)
    app.run(debug=True, port=5000) 